package scalab.caches.ccp

import scalab.core.{Cache, CacheCorePolicy}
import scalab.core.dependencies.{Dependency, SoftDependency, HardDependency}
import java.io.FileFilter
import scalab.utils.FileUtils

/**
* Defines a simple conservative cache core policy. 
*/

trait SimpleConservativeCCP extends CacheCorePolicy{

	import scalab.core.component.{Task, Filter}
	import java.io.File

	//todo: implement cache for filters
	def load(filter: Filter): Boolean = true 
	def store(filter: Filter): Boolean = true

	def versionDirectory(task: Task, sources: Iterable[Dependency]): String =
		taskDirectory(task) + dependenciesHash(sources) + File.separator//bug: include information about inputs!
	final def manifest(versionDir: String): String =
		versionDir.substring(0, versionDir.length - 1) + ".cache"

	def load(task: Task): Boolean = {
		import java.util.{Properties, Iterator}
		import java.util.Map.Entry
		import java.io.FileReader
		import scala.collection.immutable.{Set, HashSet}

		setTaskProductDir(task, versionDirectory(task, task.sources))
		val (onlyHits, opt) = getRunProducts(task, task.sources)
		if(onlyHits && !opt.isEmpty){
			//warning("found " + task.name + " in cache!")
			notifyUsed(new File(task.productDir)) //notify version used for LFU-like CEPs
			task.out.setDeps(opt.get)
			false
		}
		else{
			task.invalidateSources
			Cache.createProductDirectory(task)
			true
		}

		
	}

	def store(task: Task): Boolean = {
		import java.util.Properties
		import java.io.FileWriter
		//warning("store")
		val file: File = new File(manifest(task.productDir))
		try{
			file.getParentFile.mkdirs
			val props = new Properties
			val fw = new FileWriter(file)
			task.productDependencies foreach {d =>
				props.setProperty(d.canonicalPath, d.getType)
			}
			props.store(fw, null)
			fw.close //in finally block
			true
		}
		catch{
			//display warning
			case e: Exception => task.warning("Failed to store my work in cache:\n" + e.getMessage)  
				false
		}
	}

	def getCachedProductDirs(taskDir: File): Iterable[File] = {
		val filter: FileFilter = new FileFilter{
			def accept(file: File): Boolean = file.isDirectory
		}
		taskDir.listFiles(filter)
	}

	def free(productDirectory: File): long = {
		val manFile = new File(manifest(productDirectory.getPath))
		var deletedSize: long = manFile.length
		manFile.delete //manifest
		deletedSize += FileUtils.dirSize(productDirectory)
		FileUtils.rmDir(productDirectory)
		deletedSize
	}

	def getRunProducts(task: Task, sources: Iterable[Dependency]): Pair[Boolean, Option[Set[Dependency]]] = {
		import java.util.{Properties, Iterator}
		import java.util.Map.Entry
		import java.io.FileReader
		import scala.collection.immutable.{Set, HashSet}

		val props = new Properties
		
		try{
			
			val man = manifest(versionDirectory(task, sources))
			val manifestFile = new File(man)
			//warning("manifest: " + manifestFile.getCanonicalPath)
			if(!manifestFile.exists)
				(false, None)
			else{
				//warning("found " + task.name + "'s manifest")
				val fr = new FileReader(man)
				var deps: Set[Dependency] = HashSet.empty[Dependency]
				props.load(fr)
				fr.close //put this in finally block?
				val it: Iterator[Entry[Object, Object]] = props.entrySet.iterator
				while(it.hasNext){
					
					val entry: Entry[Object, Object] = it.next
					deps = deps + (if(entry.getValue.asInstanceOf[String] == SoftDependency.dependencyType)
							SoftDependency(entry.getKey.toString) 
						else 
							HardDependency(entry.getKey.toString))
				}
				//warning("found deps: " + deps)
				if(deps exists {_.lacks})//missing product
					(false, None)
				else//all products found => no need to execute
					(true, Some(deps))
			}
		}
		catch{
			//display warning
			case e: Exception => 
				task.error("Failed to read cache:\n" + e.getMessage)
				(false, None)
		}
	}
}
